use std::marker::PhantomPinned;
use std::pin::Pin;
use std::ptr::NonNull;

use ouroboros::self_referencing;

use crate::gen_title;

pub fn ch4_11_1() {
    gen_title("ch4_11_1", 18);

    #[derive(Debug)]
    struct WhatAboutThis<'a> {
        name: String,
        nickname: Option<&'a str>,
    }

    let mut tricky = WhatAboutThis {
        name: "Annabelle".to_string(),
        nickname: None,
    };
    tricky.nickname = Some(&tricky.name[..4]);

    println!("{:?}", tricky);
}

pub fn ch4_11_2() {
    gen_title("ch4_11_2", 18);

    #[derive(Debug)]
    struct WhatAboutThis<'a> {
        name: String,
        nickname: Option<&'a str>,
    }

    impl<'a> WhatAboutThis<'a> {
        fn tie_the_knot(&'a mut self) {
            self.nickname = Some(&self.name[..4]);
        }
    }

    let mut tricky = WhatAboutThis {
        name: "Annabelle".to_string(),
        nickname: None,
    };
    tricky.tie_the_knot();

    // cannot borrow `tricky` as immutable because it is also borrowed as mutable
    // println!("{:?}", tricky);
}

pub fn ch4_11_3() {
    gen_title("ch4_11_3", 18);

    #[derive(Debug)]
    struct SelfRef {
        value: String,
        pointer_to_value: *const String,
    }

    impl SelfRef {
        fn new(txt: &str) -> Self {
            SelfRef {
                value: String::from(txt),
                pointer_to_value: std::ptr::null(),
            }
        }

        fn init(&mut self) {
            let self_ref: *const String = &self.value;
            self.pointer_to_value = self_ref;
        }

        fn value(&self) -> &str {
            &self.value
        }

        fn pointer_to_value(&self) -> &String {
            assert!(
                !self.pointer_to_value.is_null(),
                "Test::b called without Test::init being called first"
            );
            unsafe { &*(self.pointer_to_value) }
        }
    }

    let mut t = SelfRef::new("hello");
    t.init();
    // 打印值和指针地址
    println!("{}, {:p}", t.value(), t.pointer_to_value());
}

pub fn ch4_11_4() {
    gen_title("ch4_11_4", 18);

    #[derive(Debug)]
    struct SelfRef {
        value: String,
        pointer_to_value: *mut String,
    }

    impl SelfRef {
        fn new(txt: &str) -> Self {
            SelfRef {
                value: String::from(txt),
                pointer_to_value: std::ptr::null_mut(),
            }
        }

        fn init(&mut self) {
            let self_ref: *mut String = &mut self.value;
            self.pointer_to_value = self_ref;
        }

        fn value(&self) -> &str {
            &self.value
        }

        fn pointer_to_value(&self) -> &String {
            assert!(
                !self.pointer_to_value.is_null(),
                "Test::b called without Test::init being called first"
            );
            unsafe { &*(self.pointer_to_value) }
        }
    }

    let mut t = SelfRef::new("hello");
    t.init();
    println!("{}, {:p}", t.value(), t.pointer_to_value());

    t.value.push_str(", world");
    unsafe {
        (&mut *t.pointer_to_value).push_str("!");
    }

    println!("{}, {:p}", t.value(), t.pointer_to_value());
}

pub fn ch4_11_5() {
    gen_title("ch4_11_5", 18);

    struct Unmovable {
        data: String,
        slice: NonNull<String>,
        _pin: PhantomPinned,
    }

    impl Unmovable {
        // 为了确保函数返回时数据的所有权不会被转移，我们将它放在堆上，唯一的访问方式就是通过指针
        fn new(data: String) -> Pin<Box<Self>> {
            let res = Unmovable {
                data,
                // 只有在数据到位时，才创建指针，否则数据会在开始之前就被转移所有权
                slice: NonNull::dangling(),
                _pin: PhantomPinned,
            };
            let mut boxed = Box::pin(res);

            let slice = NonNull::from(&boxed.data);
            // 这里其实安全的，因为修改一个字段不会转移整个结构体的所有权
            unsafe {
                let mut_ref: Pin<&mut Self> = Pin::as_mut(&mut boxed);
                Pin::get_unchecked_mut(mut_ref).slice = slice;
            }
            boxed
        }
    }

    let unmoved = Unmovable::new("hello".to_string());
    // 只要结构体没有被转移，那指针就应该指向正确的位置，而且我们可以随意移动指针
    let mut still_unmoved = unmoved;
    assert_eq!(still_unmoved.slice, NonNull::from(&still_unmoved.data));

    println!("{}, {:p}", still_unmoved.data, still_unmoved.slice);

    // 因为我们的类型没有实现 `Unpin` 特征，下面这段代码将无法编译
    // let mut new_unmoved = Unmovable::new("world".to_string());
    // std::mem::swap(&mut *still_unmoved, &mut *new_unmoved);
}

pub fn ch4_11_6() {
    gen_title("ch4_11_6", 18);

    #[self_referencing]
    struct SelfRef {
        value: String,

        #[borrows(value)]
        pointer_to_value: &'this str,
    }

    let v = SelfRefBuilder {
        value: "aaa".to_string(),
        pointer_to_value_builder: |value: &String| value,
    }
    .build();

    let s = v.borrow_value();
    let p = v.borrow_pointer_to_value();

    assert_eq!(s, *p);
    println!("{}, {:p}", s, p);
}

pub fn ch4_11_7() {
    gen_title("ch4_11_7", 18);

    #[self_referencing]
    struct MyStruct {
        int_data: i32,
        float_data: f32,
        #[borrows(int_data)]
        int_reference: &'this i32,
        #[borrows(mut float_data)]
        float_reference: &'this mut f32,
    }

    let mut my_value = MyStructBuilder {
        int_data: 42,
        float_data: 3.14,
        int_reference_builder: |int_data: &i32| int_data,
        float_reference_builder: |float_data: &mut f32| float_data,
    }
    .build();

    println!(
        "{:?}, {:?}",
        my_value.borrow_int_reference(),
        my_value.borrow_float_reference()
    );

    my_value.with_mut(|fields| {
        **fields.float_reference = (**fields.int_reference as f32) * 2.0;
    });

    let int_ref = *my_value.borrow_int_reference();
    println!("{:?}", *int_ref);
    drop(my_value);
}
